/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version.  The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* OpenSceneGraph Public License for more details.
*/

#ifndef OSG_OPERATIONTHREAD
#define OSG_OPERATIONTHREAD 1

#include "kernel/dll_cfg.h"
#include "kernel/observer_ptr.h"
#include "kernel/Object.h"
#include "kernel/Referenced.h"
#include "kernel/ref_ptr.h"
#include "kernel/threading/Atomic.h"
#include "kernel/threading/mutex.h"
#include "kernel/threading/thread.h"
#include <list>
#include <set>

namespace FD 
{
	namespace Kernel
	{


		//class RefBlock : virtual public CReferenced, public OpenThreads::Block
		//{
		//public:

		//	RefBlock():
		//	  CReferenced(true) {}

		//};

		//class RefBlockCount : virtual public CReferenced, public OpenThreads::BlockCount
		//{
		//public:

		//	RefBlockCount(unsigned blockCount):
		//	  CReferenced(true),
		//		  OpenThreads::BlockCount(blockCount) {}

		//};

		/** Base class for implementing graphics operations.*/
		class Operation : virtual public CReferenced
		{
		public:

			Operation(const std::string& name, bool keep):
			  CReferenced(true),
				  _name(name),
				  _keep(keep) {}


			  /** Set the human readable name of the operation.*/
			  void setName(const std::string& name) { _name = name; }

			  /** Get the human readable name of the operation.*/
			  const std::string& getName() const { return _name; }

			  /** Set whether the operation should be kept once its been applied.*/
			  void setKeep(bool keep) { _keep = keep; }

			  /** Get whether the operation should be kept once its been applied.*/
			  bool getKeep() const { return _keep; }

			  /** if this operation is a barrier then release it.*/
			  virtual void release() {}

			  /** Do the actual task of this operation.*/
			  virtual void operator () (Object*) = 0;

		protected:

			Operation():
				 CReferenced(true),
					 _keep(false) {}

				 Operation(const Operation& op):
				 CReferenced(true),
					 _name(op._name),
					 _keep(op._keep) {}

				 virtual ~Operation() {}

				 std::string _name;
				 bool        _keep;
		};

		class OperationThread;

		class JAS_Kernel_Export OperationQueue : public CReferenced
		{
		public:

			OperationQueue(){};

			/** Get the next operation from the operation queue.
			* Return null ref_ptr<> if no operations are left in queue. */
			ref_ptr<Operation> getNextOperation(bool blockIfEmpty = false);

			/** Return true if the operation queue is empty. */
			bool empty();

			/** Return the num of pending operations that are sitting in the OperationQueue.*/
			unsigned int getNumOperationsInQueue();

			/** Add operation to end of OperationQueue, this will be
			* executed by the operation thread once this operation gets to the head of the queue.*/
			void add(Operation* operation);

			/** Remove operation from OperationQueue.*/
			void remove(Operation* operation);

			/** Remove named operation from OperationQueue.*/
			void remove(const std::string& name);

			/** Remove all operations from OperationQueue.*/
			void removeAllOperations();

			/** Run the operations. */
			void runOperations(Object* callingObject=0);

			/** Call release on all operations. */
			void releaseAllOperations();

			/** Release operations block that is used to block threads that are waiting on an empty operations queue.*/
			void releaseOperationsBlock();

			typedef std::set<OperationThread*> OperationThreads;

			/** Get the set of OperationThreads that are sharing this OperationQueue. */
			const OperationThreads& getOperationThreads() const { return _operationThreads; }

		protected:

			virtual ~OperationQueue();

			friend class OperationThread;

			void addOperationThread(OperationThread* thread);
			void removeOperationThread(OperationThread* thread);

			typedef std::list< ref_ptr<Operation> > Operations;

			CMutex          _operationsMutex;
			//ref_ptr<osg::RefBlock> _operationsBlock;
			Operations                  _operations;
			Operations::iterator        _currentOperationIterator;

			OperationThreads            _operationThreads;
		};

		/** OperationThread is a helper class for running Operation within a single thread.*/
		class JAS_Kernel_Export OperationThread : /*public CReferenced, */public CThread
		{
		public:
			OperationThread();

			void setParent(Object* parent) { _parent = parent; }

			Object* getParent() { return _parent.get(); }

			const Object* getParent() const { return _parent.get(); }


			/** Set the OperationQueue. */
			void setOperationQueue(OperationQueue* opq);

			/** Get the OperationQueue. */
			OperationQueue* getOperationQueue() { return _operationQueue.get(); }

			/** Get the const OperationQueue. */
			const OperationQueue* getOperationQueue() const { return _operationQueue.get(); }


			/** Add operation to end of OperationQueue, this will be
			* executed by the graphics thread once this operation gets to the head of the queue.*/
			void add(Operation* operation);

			/** Remove operation from OperationQueue.*/
			void remove(Operation* operation);

			/** Remove named operation from OperationQueue.*/
			void remove(const std::string& name);

			/** Remove all operations from OperationQueue.*/
			void removeAllOperations();


			/** Get the operation currently being run.*/
			ref_ptr<Operation> getCurrentOperation() { return _currentOperation; }

			/** Run does the opertion thread run loop.*/
			virtual int32 run();

			void setDone(bool done);

			bool getDone() const { return _done!=0; }

			/** Cancel this graphics thread.*/
			virtual int cancel();

		protected:

			virtual ~OperationThread();

			observer_ptr<Object>            _parent;

			CAtomic             _done;

			CMutex              _threadMutex;
			ref_ptr<OperationQueue>    _operationQueue;
			ref_ptr<Operation>         _currentOperation;
			
		};

		typedef OperationThread OperationsThread;

	}
}
#endif
